﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

using RioTinto.Data.Helpers;
using RioTinto.Data.Interfaces;
using RioTinto.Helpers;
using Microsoft.Practices.Unity;

namespace RioTinto.Data.Entities
{
    public class BaseEntity<T> where T : BaseEntity<T>
    {
        private static IDataManager<T> m_Manager;

        /// <summary>
        /// Gets global (Singleton) DataManager object for
        /// type T.
        /// </summary>
        private static IDataManager<T> Manager
        {
            get
            {
                if (m_Manager != null)
                    return m_Manager;

                UnityContainer container = DependencyContainer.Container;
                try
                {
                    m_Manager = container.Resolve<IDataManager<T>>();
                }
                // Unity doesn't support TryResolve, so we're doing it manually
                // this exception should only ever be raised once regardless
                // of the number of enties in the data model
                catch (ResolutionFailedException)
                {
                    // IDataManager<T> hasn't been explicitly registered, so register 
                    // the default one
                    container.RegisterType(typeof(IDataManager<>), typeof(DataManager<>),
                        new ContainerControlledLifetimeManager());

                    // now we can resolve the reference
                    m_Manager = container.Resolve<IDataManager<T>>();
                }

                return m_Manager;
            }
        }

        public BaseEntity()
        {

        }

        /// <summary>
        /// Saves this entity to the database.
        /// </summary>
        public void Save()
        {
            Manager.Save((T)this);
        }

        /// <summary>
        /// Deletes this entity from the database.
        /// </summary>
        public void Delete()
        {
            Manager.Delete((T)this);
        }
        /// <summary>
        /// Gets a fresh copy of this entity from the database.
        /// </summary>
        public void Refresh()
        {
            Manager.Refresh((T)this);
        }

        /// <summary>
        /// Gets all items from the database matching the provided selection criteria.
        /// </summary>
        public static void DeleteAll(IEnumerable<T> list)
        {
            Manager.DeleteAll(list);
        }

        /// <summary>
        /// Gets one entity from the database.
        /// </summary>
        public static T GetOne(Int32 id)
        {
            return Manager.GetOne(id);
        }

        /// <summary>
        /// Gets all items from the database.
        /// </summary>
        public static IList<T> GetAll()
        {
            return Manager.GetAll();
        }

        /// <summary>
        /// Gets all items from the database matching the provided selection criteria.
        /// </summary>
        public static IList<T> GetAll(Expression<Func<T, bool>> predicate)
        {
            return Manager.GetAll(predicate);
        }

        /// <summary>
        /// Gets an IQueryable reference for use in a custom query and
        /// for delayed execution
        /// </summary>
        /// <returns></returns>
        public static IQueryable<T> GetQueryable()
        {
            return Manager.GetQueryable();
        }

        /// <summary>
        /// Gets an IQueryable reference for use in a custom query and
        /// for delayed execution
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static IQueryable<T> GetQueryable(Expression<Func<T, Boolean>> predicate)
        {
            return Manager.GetQueryable(predicate);
        }

        /// <summary>
        /// Gets the value of the primary key of the given entity
        /// </summary>
        /// <remarks>
        /// Used to simplify testing and allow generic methods to 
        /// look up primary key value.
        /// </remarks>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static Int32 GetPrimaryKeyValue(T entity)
        {
            return EntityReflector.GetPrimaryKeyValue(entity);
        }

        /// <summary>
        /// Sets the value of the primary key of the given entity
        /// </summary>
        /// <remarks>
        /// Used to simplify testing and allow generic methods to 
        /// set primary key value.
        /// </remarks>
        /// <param name="entity"></param>
        /// <param name="value"></param>
        public static void SetPrimaryKeyValue(T entity, Int32 value)
        {
            EntityReflector.SetPrimaryKeyValue(entity, value);
        }
    }
}
